package com.yygo.service;

import com.yygo.Constants;
import com.yygo.model.LoanRequest;
import com.yygo.model.User;
import com.yygo.model.enums.Phase;
import com.yygo.model.enums.RequestStatus;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;

import java.lang.reflect.Method;
import java.util.Collection;

/**
 * Created by tiansha on 2015/7/25.
 */
public class LoanRequestSecurityAdvice implements MethodBeforeAdvice {

    public static final String DEALER_ACCESS_DENIED = "Access Denied: Dealer users are allowed to modify their own loan request.";
    public static final String AUDIT_ACCESS_DENIED = "Access Denied: Audit users are not allowed to modify draft loan request.";
    public static final String BOND_ACCESS_DENIED = "Access Denied: Bond users are not allowed to modify other bond's loan request.";
    public static final String BANK_ACCESS_DENIED = "Access Denied: Bank users are not allowed to modify other bank's loan request.";

    private final Log log = LogFactory.getLog(LoanRequestSecurityAdvice.class);

    public void before(Method method, Object[] args, Object target) throws Throwable {
        SecurityContext ctx = SecurityContextHolder.getContext();
        if (ctx.getAuthentication() != null) {
            Authentication auth = ctx.getAuthentication();
            boolean isDealer = false;
            boolean isBank = false;
            boolean isBond = false;
            Collection<? extends GrantedAuthority> roles = auth.getAuthorities();
            for (GrantedAuthority role : roles) {
                if (role.getAuthority().equals(Constants.DEALER_USER_ROLE)) {
                    isDealer = true;
                    break;
                } else if (role.getAuthority().equals(Constants.Material_Print_ROLE) ||role.getAuthority().equals(Constants.Material_Print_ROLE)||role.getAuthority().equals(Constants.Material_Audit_ROLE) ||role.getAuthority().equals(Constants.Material_Receive_ROLE) ||role.getAuthority().equals(Constants.Material_Audit_ROLE) ||role.getAuthority().equals(Constants.Bank_USER_ROLE) || role.getAuthority().equals(Constants.Bank_CREDIT_ROLE)) {
                    isBank = true;
                    break;
                } else if (role.getAuthority().equals(Constants.Bond_USER_ROLE)) {
                    isBond = true;
                    break;
                }
            }

            LoanRequest loanRequest = (LoanRequest) args[0];

            AuthenticationTrustResolver resolver = new AuthenticationTrustResolverImpl();
            // allow new users to signup - this is OK b/c Signup doesn't allow setting of roles
            boolean signupUser = resolver.isAnonymous(auth);

            if (!signupUser) {
                LoanRequestManager loanRequestManager = (LoanRequestManager) target;
                User currentUser = getCurrentUser(auth);
                if (isDealer) {
                    if (currentUser.getDealer() == null || !currentUser.equals(loanRequest.getDealerUser())) {
                        throw new AccessDeniedException(DEALER_ACCESS_DENIED);
                    }
                } else if (isBond) {
                    if (!(loanRequest.getDealer().getBond().getBondUser().equals(currentUser)
                            || loanRequest.getDealer().getSlaveProvider().getBondUser().equals(currentUser) || loanRequest.getDealer().getMasterProvider().getBondUser().equals(currentUser))) {
                        throw new AccessDeniedException(BOND_ACCESS_DENIED);
                    }
                } else if (isBank) {
                    /*if (!loanRequest.getLoanProduct().getBank().getUsers().contains(currentUser)) {
                        throw new AccessDeniedException(BANK_ACCESS_DENIED);
                    }*/
                } else if (loanRequest.getPhase().equals(Phase.draft) || loanRequest.getPhase().equals(Phase.dealer_submit)) {
                    //throw new AccessDeniedException(AUDIT_ACCESS_DENIED);
                }
            }
        }
    }

    private User getCurrentUser(Authentication auth) {
        User currentUser;
        if (auth.getPrincipal() instanceof UserDetails) {
            currentUser = (User) auth.getPrincipal();
        } else if (auth.getDetails() instanceof UserDetails) {
            currentUser = (User) auth.getDetails();
        } else {
            throw new AccessDeniedException("User not properly authenticated.");
        }
        return currentUser;
    }
}
